<template>
  <div ref="canvas"></div>
</template>

<script lang="ts" setup>
import * as THREE from "three";
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";
import {ref, onMounted} from "vue";

// 画布
const canvas = ref<any>('');
// 场景
const scene = new THREE.Scene();
// 相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(-30, 40, 30);
camera.lookAt(scene.position);

// basic();

createBoundingWall();
createGroundPlane();
createHouse();
createTree();

// 光源
const spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 40, -15);
// 启用阴影
spotLight.castShadow = true;
// 阴影细节
spotLight.shadow.mapSize = new THREE.Vector2(window.innerWidth, window.innerHeight);
spotLight.shadow.camera.far = 130;
spotLight.shadow.camera.near = 40;

scene.add(spotLight);
scene.add(new THREE.SpotLightHelper(spotLight));

// 渲染器
const renderer = new THREE.WebGLRenderer({
  antialias: true,
});
renderer.setClearColor(0xEEEEEE);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// 通过设置shadowMapEnabled属性为“true”来告诉渲染器需要阴影效果
// 然后通过 castShadow 来指明哪些物体投射阴影 receiveShadow 来指明哪些物体接受阴影
renderer.shadowMap.enabled = true;
/**
 * 渲染函数
 */
const render = () => {
  renderer.render(scene, camera);
  requestAnimationFrame(render);
}
// 相机控件
const orbitControl = new OrbitControls(camera, renderer.domElement);
orbitControl.update();

onMounted(() => {
  canvas.value.appendChild(renderer.domElement);
  render();
})

function basic() {
  // 辅助坐标轴
  const axesHelper = new THREE.AxesHelper(100);
  scene.add(axesHelper);
  // 平面
  const planeGeometry = new THREE.PlaneGeometry(60, 20);
  const planeMaterial = new THREE.MeshLambertMaterial({
    color: 0xAAAAAA,
    side: THREE.DoubleSide,
  });
  const plane = new THREE.Mesh(planeGeometry, planeMaterial);
  plane.rotation.x = -0.5 * Math.PI;
  plane.position.set(15, 0, 0);
  // 指明这个平面接收阴影
  plane.receiveShadow = true;
  scene.add(plane);
  // 正方体
  const boxGeometry = new THREE.BoxGeometry(4, 4, 4);
  const boxMaterial = new THREE.MeshLambertMaterial({
    color: 0xff0000,
    side: THREE.DoubleSide,
  });
  const box = new THREE.Mesh(boxGeometry, boxMaterial);
  box.position.set(-4, 3, 0);
  // 启用阴影
  box.castShadow = true;
  scene.add(box);
  // 球体
  const sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
  const sphereMaterial = new THREE.MeshLambertMaterial({
    color: 0x7777ff,
    side: THREE.DoubleSide,
  });
  const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
  sphere.position.set(20, 4, 2);
  // 启用阴影
  sphere.castShadow = true;
  scene.add(sphere);
}

/**
 * 创建边界墙
 */
function createBoundingWall() {
  // 墙体
  const wallLeft = new THREE.BoxGeometry(70, 2, 2);
  const wallRight = new THREE.BoxGeometry(70, 2, 2);
  const wallTop = new THREE.BoxGeometry(2, 2, 50);
  const wallBottom = new THREE.BoxGeometry(2, 2, 50);
  // 墙体材质
  const wallMaterial = new THREE.MeshLambertMaterial({
    color: 0xa0522d,
    side: THREE.DoubleSide,
  });
  // 墙体网格模型Mesh
  const wallLeftMesh = new THREE.Mesh(wallLeft, wallMaterial);
  const wallRightMesh = new THREE.Mesh(wallRight, wallMaterial);
  const wallTopMesh = new THREE.Mesh(wallTop, wallMaterial);
  const wallBottomMesh = new THREE.Mesh(wallBottom, wallMaterial);
  // 墙体position设置
  wallLeftMesh.position.set(15, 1, -25);
  wallRightMesh.position.set(15, 1, 25);
  wallTopMesh.position.set(-19, 1, 0);
  wallBottomMesh.position.set(49, 1, 0);

  scene.add(wallLeftMesh, wallRightMesh, wallTopMesh, wallBottomMesh);
}

/**
 * 创建地面
 */
function createGroundPlane() {
  // 地面
  const planeGeometry = new THREE.PlaneGeometry(70, 50);
  const planeMaterial = new THREE.MeshLambertMaterial({
    color: 0x9acd32,
    side: THREE.DoubleSide,
  });
  const plane = new THREE.Mesh(planeGeometry, planeMaterial);
  plane.receiveShadow = true;
  plane.rotation.x = -0.5 * Math.PI;
  plane.position.set(15, 0, 0);
  scene.add(plane);
}

/**
 * 创建房子
 */
function createHouse() {
  // 房顶
  const roof = new THREE.ConeGeometry(5, 4);
  const roofMesh = new THREE.Mesh(roof, new THREE.MeshLambertMaterial({
    color: 0x8b7213,
  }));
  // 房子主体
  const house = new THREE.CylinderGeometry(5, 5, 6);
  const houseMesh = new THREE.Mesh(house, new THREE.MeshLambertMaterial({
    color: 0xffe4c4,
  }));

  roofMesh.position.set(25, 8, 0);
  houseMesh.position.set(25, 3, 0);

  roofMesh.receiveShadow = true;
  houseMesh.receiveShadow = true;
  roofMesh.castShadow = true;
  houseMesh.castShadow = true;

  scene.add(roofMesh, houseMesh);
}

/**
 * 创建树
 */
function createTree() {
  // 树干
  const trunk = new THREE.BoxGeometry(1, 8, 1);
  const trunkMesh = new THREE.Mesh(trunk, new THREE.MeshLambertMaterial({
    color: 0x8b4513,
  }));
  // 树叶
  const leave = new THREE.SphereGeometry(4);
  const leaveMesh = new THREE.Mesh(leave, new THREE.MeshLambertMaterial({
    color: 0x00ff00,
  }));

  trunkMesh.position.set(-10, 4, 0);
  leaveMesh.position.set(-10, 12, 0);

  trunkMesh.receiveShadow = true;
  trunkMesh.castShadow = true;
  leaveMesh.receiveShadow = true;
  leaveMesh.castShadow = true;

  scene.add(trunkMesh, leaveMesh);
}

</script>

<style scoped>
body {
  margin: 0;
  overflow: hidden;
}
</style>
